package cz.acies.utils;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.*;

//******************************************************************************
//
//			***		class DateUtility 		***
//
//******************************************************************************

public class DateUtils 
{

	public static final String TIMESTAMP_PATTERN = "\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
	public static final String DATE_PATTERN = "\\d{4}-\\d{1,2}-\\d{1,2}";
	public static final String TIME_PATTERN = "\\d{1,2}:\\d{1,2}:\\d{1,2}";

	// -----  -----------------------------------------------------------------

	public static Date getToday() {
		return new Date();	
	}

	public static Timestamp getTodaySQL() {
		return new Timestamp(System.currentTimeMillis());
	}
	
	// -----  -----------------------------------------------------------------
	
	public static java.util.Date parseDate(String s, String pattern) throws Exception {
		return org.apache.commons.lang3.time.DateUtils.parseDate(s, pattern);
	}
	
	public static Date	parseDate(String s, String... patterns) throws Exception {
		return org.apache.commons.lang3.time.DateUtils.parseDate(s, patterns);
	}
	
	public static Timestamp stringToSQLDate(String dateStr) throws Exception {
		Timestamp date = null;
		try {
			if (dateStr == null || dateStr.length() == 0) {
				dateStr = "1900-01-01 00:00:00";
			}
			if (dateStr.length() > 19) {
				dateStr = dateStr.substring(0, 19); 
			}
			date = Timestamp.valueOf(dateStr);	
		} catch (Exception e) {
			return Timestamp.valueOf("1900-01-01 00:00:00"); 
		}
		if (date == null) date = Timestamp.valueOf("1900-01-01 00:00:00");
		return date;
	}
		
	public static String formatSQLDate(String value, int type) throws Exception {
		try {
			switch (type) { 
				case java.sql.Types.DATE: {
					value = parseSQLDate(value);
					if (value == null) {
						value = "1900-01-01";
					}
					java.sql.Date date = null;
					try {
						date = java.sql.Date.valueOf(value);	
					} catch (Exception e) {
						date = java.sql.Date.valueOf("1900-01-01"); 
					}
					long tm = date.getTime();
					if (tm < -2208992399000L) tm = -2208992399000L;

					date.setTime(tm);
					value = date.toString().substring(0, 10);					
					return parseSQLDate(value);
				}
				case java.sql.Types.TIMESTAMP:
				default: {
					value = DateUtils.formatSQLDate(value);
					break;
				}
			}
		} catch (Exception exc) {
			exc.printStackTrace();
			return parseSQLDateTime("1900-01-01 00:00:01");
		}
		return parseSQLDateTime(value);
	}

	public static String formatSQLDate(String value) throws Exception {
		try {
			value = parseSQLDateTime(value);
			if (value == null) {
				value = "1900-01-01 00:00:01";
			}
			Timestamp date = null;
			try {
				date = Timestamp.valueOf(value);	
			} catch (Exception e) {
				date = Timestamp.valueOf("1900-01-01 00:00:01"); 
			}
			long tm = date.getTime();
			if (tm < -2208992399000L) tm = -2208992399000L;

			date.setTime(tm);
			value = date.toString().substring(0, 19);			
		} catch (Exception exc) {
			exc.printStackTrace();
			return parseSQLDateTime("1900-01-01 00:00:01");
		}
		return parseSQLDateTime(value);
	}
	
	public static String formatSQLDateZeroSec(String datestr) throws Exception {
		if (isTimestampValid(datestr)) {
			datestr = datestr.substring(0, 17)+"00";
		}
		return datestr;
	}

	public static String parseSQLDateTime(String date) throws Exception {
		String value = null;
		try {
			if (!isTimestampValid(date)) {
				if (isDateValid(date)) {
					date += " 00:00:00";
				}
			}
			Pattern pattern = Pattern.compile(TIMESTAMP_PATTERN);
			Matcher matcher = pattern.matcher(date);
			while (matcher.find()) {
				value = matcher.group();
			}	
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return value;
	}
	
	public static boolean isTimestampValid(String date) {
		if (date == null) return false;
		return date.matches(TIMESTAMP_PATTERN);
	}

	public static String parseSQLDate(String date) throws Exception {
		String value = null;
		try {
			Pattern pattern = Pattern.compile(DATE_PATTERN);
			Matcher matcher = pattern.matcher(date);
			while (matcher.find()) {
				value = matcher.group();
			}	
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return value;
	}
	
	public static boolean isDateValid(String date) {
		if (date == null) return false;
		return date.matches(DATE_PATTERN);
	}

	public static boolean isTimeValid(String date) {
		if (date == null) return false;
		return date.matches(TIME_PATTERN);

	}

	/**
	 * statick� metoda p�i�te k datu ve form�tu "unicode date" po�et hodin v z�vislosti
	 * na �asov�m p�smu a lestn�m �ase.
	 * 
	 * @param datestr
	 * @return
	 * @throws Exception
	 */
	public static Timestamp unicodeDate2timezoneDate(String datestr) throws Exception {
		Timestamp date = Timestamp.valueOf(datestr);
		return unicodeDate2timezoneDate(date.getTime());
	}

	/**
	 * statick� metoda p�i�te k datu ve form�tu "unicode date" po�et hodin v z�vislosti
	 * na �asov�m p�smu a lestn�m �ase.
	 * 
	 * @param date
	 * @return
	 * @throws Exception
	 */
	public static Timestamp unicodeDate2timezoneDate(Date date) throws Exception {
		return unicodeDate2timezoneDate(date.getTime());
	}
	
	/**
	 * statick� metoda p�i�te k datu ve form�tu "unicode date" po�et hodin v z�vislosti
	 * na �asov�m p�smu a lestn�m �ase.
	 *  
	 * @param time
	 * @return
	 * @throws Exception
	 */
	public static Timestamp unicodeDate2timezoneDate(long time) throws Exception {
		Calendar cal = Calendar.getInstance();
		long offset = cal.getTimeZone().getOffset(time);
		time = time + offset;
		return new Timestamp(time);
	}

	public static Timestamp timezoneDate2unicodeDate(long time) throws Exception {
		Calendar cal = Calendar.getInstance();
		long offset = cal.getTimeZone().getOffset(time);
		time = time - offset;
		return new Timestamp(time);
	}

	// ------  ----------------------------------------------------------------

	public static String todayToFormat(String pattern) throws Exception {
		SimpleDateFormat fmt = new SimpleDateFormat(pattern);
		return fmt.format(new Date()).toString();
	}

	public static String todayToSQLFormat() throws Exception {
		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		return fmt.format(new Date()).toString();		
	}
	
	public static String dateToFormat(Date date, String pattern) throws Exception {
		SimpleDateFormat fmt = new SimpleDateFormat(pattern);
		return fmt.format(date).toString();
	}
	
	public static String dateToSQLFormat(Date date) throws Exception {
		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		return fmt.format(date).toString();		
	}

	public static String dateToSQLFormat(String date) throws Exception {
		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		return fmt.format(date).toString();		
	}

	// ------  ----------------------------------------------------------------
	
	public static Date addMinute(Date date, int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		calendar.add(Calendar.MINUTE, amount);
		date = calendar.getTime();
		return date;
	}

	public static Date addHour(Date date, int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		calendar.add(Calendar.HOUR, amount);
		date = calendar.getTime();
		return date;
	}

	/**
	 * Metoda vraci datum opravene o pocet dnu, vypocteneho z data zadaneho 
	 * paramtrem date. Pokud je paramtr amount kladne cislo jsou dny pridany, 
	 * pokud je zaporne jsou dny odecteny.  
	 * @param amount
	 * @return
	 * @throws Exception
	 */
	public static Date addDay(Date date, int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		calendar.add(Calendar.DAY_OF_MONTH, amount);
		date = calendar.getTime();
		return date;
	}

	/**
	 * Metoda vraci datum opravene o pocet dnu, vypocteneho aktualniho casu. 
	 * Pokud je paramtr amount kladne cislo jsou dny pridany, pokud je zaporne
	 * jsou dny odecteny.  
	 * @param amount
	 * @return
	 * @throws Exception
	 */
	public static Date addDay(int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.add(Calendar.DAY_OF_MONTH, amount);
		Date date = calendar.getTime();
		return date;
	}

	public static int getDayOfMonth() throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		return calendar.get(Calendar.DAY_OF_MONTH);
	}

	public static int getDayOfMonth(Date date) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		return calendar.get(Calendar.DAY_OF_MONTH);
	}	

	/**
	 * Metoda vraci numerickou hodnotu mesice k aktualnimu datu. 
	 * @return
	 * @throws Exception
	 */
	public static int getMonth() throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		return calendar.get(Calendar.MONTH) + 1;
	}

	/**
	 * Metoda vraci numerickou hodnotu mesice k datu zadan�mu parametrem date. 
	 * @return
	 * @throws Exception
	 */
	public static int getMonth(Date date) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		return calendar.get(Calendar.MONTH) + 1;
	}

	/**
	 * Metoda vraci datum opravene o pocet mesicu, vypocteneho z data zadaneho 
	 * paramtrem date. Pokud je paramtr amount kladne cislo jsou mesice pridany, 
	 * pokud je zaporne jsou mesice odecteny.
	 *   
	 * @param amount
	 * @return
	 * @throws Exception
	 */
	public static Date addMonth(Date date, int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		calendar.add(Calendar.MONTH, amount);
		date = calendar.getTime();
		return date;
	}

	public static Date addMonth(Calendar calendar, int amount) throws Exception {
		calendar.add(Calendar.MONTH, amount);
		return calendar.getTime();
	}
	
	/**
	 * Metoda vraci datum opravene o pocet mesicu, vypocteneho aktualniho casu. 
	 * Pokud je paramtr amount kladne cislo jsou mesice pridany, pokud je zaporne 
	 * jsou mesice odecteny.
	 *   
	 * @param amount
	 * @return
	 * @throws Exception
	 */
	public static Date addMonth(int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.add(Calendar.MONTH, amount);
		Date date = calendar.getTime();
		return date;
	}


	/**
	 * Metoda vraci numerickou hodnotu roku k aktualnimu datu. 
	 * @return
	 * @throws Exception
	 */
	public static int getYear() throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		return calendar.get(Calendar.YEAR);
	}

	/**
	 * Metoda vraci numerickou hodnotu roku k datu zadan�mu parametrem date. 
	 * @return
	 * @throws Exception
	 */
	public static int getYear(Date date) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		return calendar.get(Calendar.YEAR);
	}

	public static Date addYear(Date date, int amount) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		calendar.add(Calendar.YEAR, amount);
		date = calendar.getTime();
		return date;
	}

	/**
	 * Metoda vraci poradove cislo dne v roce
	 * @param date
	 * @return
	 * @throws Exception
	 */
	public static int getDayOfYear(Date date) throws Exception {
		Calendar calendar = Calendar.getInstance() ;
		calendar.setTime(date);
		return calendar.get(Calendar.DAY_OF_YEAR);
	}	

	//----- Time methods ------------------------------------------------------

	/**
	 * statick� pomocn� metoda longToTime p�evede, ��selnou hodnotu prezentuj�c� 
	 * �as v milisekund�ch, na text v �asov�m form�tu HH:mm:ss.
	 * 
	 * @param i - ��seln� hodnota prezentuj�c� �as v milisekund�ch
	 * @return vraci text v �asov�m form�tu HH:mm:ss.
	 */
	public static String longToTime(long millis) {
		try {
			//return new Time((millis*1000)-3600000).toString();
			DateFormat df = new SimpleDateFormat("HH:mm:ss");
			df.setTimeZone(TimeZone.getTimeZone("GMT+0"));
			return df.format(new Timestamp(millis));
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return "00:00:00";
	}

	/**
	 * statick� pomocn� metoda timeToLong p�evede, stringovou hodnotu prezentuj�c� 
	 * �as ve form�tu HH:mm:ss, na na ��selnou hodnotu.
	 * 
	 * @param String s
	 * @return
	 */
	public static long timeToLong(String s) {
		try {			
			DateFormat df = new SimpleDateFormat("HH:mm:ss");
			df.setTimeZone(TimeZone.getTimeZone("GMT+0"));
			long time = df.parse(s).getTime();
			return time;
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return 0;
	}
	
	/**
	 * statick� pomocn� metoda intToTime p�evede, ��selnou hodnotu prezentuj�c� 
	 * �as v milisekund�ch, na text v �asov�m form�tu HH:mm:ss.
	 * 
	 * @param i - ��seln� hodnota prezentuj�c� �as v milisekund�ch
	 * @return vraci text v �asov�m form�tu HH:mm:ss.
	 */
	public static String intToTime(int i) {
		try {
			return new Time((i*1000)-3600000).toString();	
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return "00:00:00";
	}

	/**
	 * statick� pomocn� metoda intToTime p�evede objetovou ��selnou hodnotu na 
	 * hodnotu bin�rn�ho typu a vol� metodu intToTime(int i). 
	 * 
	 * @param Integer i - objektov� ��seln� hodnota prezentuj�c� �as v milisekund�ch
	 * @return vraci string
	 */
	public static String intToTime(Integer i) {
		return intToTime(i.intValue());
	}

	/**
	 * statick� pomocn� metoda intToTime p�evede stringovou hodnotu na 
	 * hodnotu bin�rn�ho typu a vol� metodu intToTime(int i). 
	 * 
	 * @param String i - stringov� ��seln� hodnota prezentuj�c� �as v milisekund�ch
	 * @return vraci string
	 */
	public static String intToTime(String s) {
		try {
			int i = new Integer(s).intValue();
			return intToTime(i);
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return "00:00:00";
	}
		
	// -----  -----------------------------------------------------------------
	
	public static String formatCurrentTime() throws Exception {
		SimpleDateFormat dfmt = new SimpleDateFormat("HH:mm:ss");
		Date time = new Date();
		//System.out.println("formatCurrentTime().time = "+dfmt.format(time));
		return dfmt.format(time);
	}

	public static String formatCurrentTime(String pattern) throws Exception {
		try {
			SimpleDateFormat dfmt = new SimpleDateFormat(pattern);
			Date time = new Date();
			//System.out.println("formatCurrentTime( "+pattern+" ).time = "+dfmt.format(time));		
			return dfmt.format(time);
		} catch(Exception ex){
			return formatCurrentTime();
		}
	}	
	
	// -----  -----------------------------------------------------------------

	public static String stringMaxDateOfMonth(Timestamp date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		int month = calendar.get(Calendar.MONTH);
		int year = calendar.get(Calendar.YEAR);
		int day = getMonthDays(year, month);
		calendar.set(year, month, day, 23, 59, 59);
		return new Timestamp(calendar.getTimeInMillis()).toString();
	}

	public static String stringMaxDateOfMonth(long time) {
		Timestamp date = new Timestamp(time);
		return DateUtils.stringMaxDateOfMonth(date);
	}

	public static String stringMaxDateOfMonth(String datestr) {
		Timestamp date = Timestamp.valueOf(datestr);
		return DateUtils.stringMaxDateOfMonth(date);
	}
	
	/**
	 * Statick� metoda pro zji�t�n� maxin�ln�ho po�tu dn� v m�s�c� dan�ho roku
	 * Parametr month je t�eba zad�vat, tak jak ho vrac� t��da Calendar tj. ledem = 0
	 *  
	 * @param year
	 * @param month
	 * @return
	 */
	public static int getMonthDays(int year, int month) {
		switch (month) {
			case 3:
			case 5:
			case 8:
			case 10: {
				return 30;
			}
			case 1: {
				return year % 4 == 0 && year % 100 != 0 ? 29 : 28;
			}
			default: {
				return 31;
			}
		}
	}

	// -----  -----------------------------------------------------------------
	
	public static Date firtsDayOfWeek(Date day) {
		Calendar c = Calendar.getInstance();
		c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		c.setTime(day);
		
		int dayOfWeek = c .get(Calendar.DAY_OF_WEEK);
		int sub = 0; 
		switch (dayOfWeek) {
			case 1: sub = -6; 	break;
			case 2: sub =  0; 	break;
			case 3: sub = -1; 	break;
			case 4: sub = -2; 	break;
			case 5: sub = -3; 	break;
			case 6: sub = -4; 	break;
			case 7: sub = -5; 	break;
			default:
				break;
		}
		c.add(Calendar.DAY_OF_MONTH, sub);
		return c.getTime();
	}
	
	public static Date latsDayOfWeek(Date day) {
		Date firstDate = DateUtils.firtsDayOfWeek(day);
		Calendar c = Calendar.getInstance();
		c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		c.setTime(firstDate);
		c.add(Calendar.DATE, 7);
		
		return firtsDayOfWeek(new Timestamp(c.getTimeInMillis()));
	}

	public static List<String> weekDays(Date day) {
		List<String> days = new ArrayList<String>();
		Date fisrtDay = firtsDayOfWeek(day);
		Calendar c = Calendar.getInstance();
		c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		c.setTime(fisrtDay);
		
		DateFormat df = new SimpleDateFormat(/*"EEE " + */"yyyy-MM-dd");
		for (int i = 0; i < 7; i++) {
			days.add(df.format(c.getTime()));
			c.add(Calendar.DATE, 1);
		}		
		return days;
	}

	// ------  ----------------------------------------------------------------

	public static Date firstDayOfMonth(Date day) {		
		Calendar c = Calendar.getInstance();
		c.setTime(day);
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH);
		GregorianCalendar gc = new GregorianCalendar(year, month, 1);
		return new Timestamp(gc.getTimeInMillis());
	}
	
	public static Date lastDayOfMonth(Date day) {		
		Calendar c = Calendar.getInstance();
		c.setTime(day);
		
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH);
		int max = getMonthDays(year, month);
		
		GregorianCalendar gc = new GregorianCalendar(year, month, max);
		return new Timestamp(gc.getTimeInMillis());
	}

	// -----  -----------------------------------------------------------------
	
	/**
	 * Metoda vraci datum posledniho dme v roce urcenho parametrem day
	 * @param day
	 * @return
	 */
	public static Date lastDayOfYear(Date day) {		
		Calendar c = Calendar.getInstance();
		c.setTime(day);
		
		int year = c.get(Calendar.YEAR);
		int month = 11;
		int max = 31;
		
		GregorianCalendar gc = new GregorianCalendar(year, month, max);
		return new Timestamp(gc.getTimeInMillis());
	}

};
